package com.bb.blog.security.controller;


import com.bb.blog.web.component.ResponseUtil;
import com.bb.blog.web.constant.CommonServerExceptionEnum;
import com.bb.blog.web.model.CommonResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AccountStatusException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.security.Principal;
import java.util.HashMap;
import java.util.Map;


@RestController
@Slf4j
@RequiredArgsConstructor
public class TokenController {
    private final TokenEndpoint tokenEndpoint;

    /**
     * 1. password 的认证流程
     * 1.1 ClientCredentialsTokenEndpointFilter 去校验Client端的身份信息是否正确
     * 1.1.1 AuthenticationManager 生成 Authentication
     * 1.2 进入TokenEndpoint 前判断是否认证过，并校验身份信息与请求的信息是否一致
     * 1.3 Oauth2RequestFactory 去包装登录参数信息，并校验当前登录用户是否有请求需要的scope权限
     * 1.4 使用TokenGranter.grant授权
     * 1.5 再次鉴权
     * 1.6 TokenService getAccessToken
     * 1.6.1 如果AccessToken过期 清除AccessToken和RefreshToken
     * 1.6.2 如果没有过期 重新存储一遍防止Authentication改动
     */


    @RequestMapping(value = "/oauth/token", method = {RequestMethod.GET, RequestMethod.POST})
    public CommonResponse getAccessToken(Principal principal, @RequestParam
            Map<String, String> parameters) {

        try {
            ResponseEntity<OAuth2AccessToken> oAuth2AccessTokenResponseEntity = tokenEndpoint.postAccessToken(principal, parameters);
            OAuth2AccessToken body = oAuth2AccessTokenResponseEntity.getBody();
            return ResponseUtil.success(body);

        } catch (AccountStatusException ase) {
            //covers expired, locked, disabled cases (mentioned in section 5.2, draft 31)
            log.error("用户状态异常 ----------> ", ase);
            return ResponseUtil.fail(CommonServerExceptionEnum.ACCOUNT_STATUS_EXCEPTION);
        } catch (BadCredentialsException e) {
            // If the username/password are wrong the spec says we should send 400/invalid grant
            log.error("账号或密码错误 ----------> ", e);
            return ResponseUtil.fail(CommonServerExceptionEnum.BAD_CREDENTIALS);
        } catch (InvalidGrantException e) {
            log.error("Token无效 ---------->", e);
            return ResponseUtil.fail(CommonServerExceptionEnum.TOKEN_GRANT_EXCEPTION);
        } catch (InvalidTokenException e) {
            log.error("Token已经过期 ---------->", e);
            return ResponseUtil.fail(CommonServerExceptionEnum.TOKEN_INVALID);
        } catch (Exception e) {
            log.error("认证异常 ----------> ", e);
            return ResponseUtil.fail(CommonServerExceptionEnum.SECURITY_EXCEPTION);
        }


    }


}
